package com.liuyjy.redis.util;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static org.springframework.data.redis.connection.RedisStringCommands.SetOption.UPSERT;

/**
 * @author: liuyjy
 * @Description: redis封装方法
 * @date: 2022/2/10 16:34
 */
@Slf4j
@Configuration
public class RedisCacheImpl implements RedisCache {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 获取getRedisTemplate()对象
     *
     * @return getRedisTemplate()<String, Object>
     */
    @Override
    public RedisTemplate<String, Object> getRedisTemplate() {
        if (redisTemplate == null) {
            log.error("getRedisTemplate() is not initialized");
            throw new IllegalStateException("getRedisTemplate() is not initialized");
        }
        return redisTemplate;
    }

    /**
     * 获取ListOperations对象
     *
     * @return ListOperations
     */
    @Override
    public ListOperations<String, Object> getListOperations() {
        return getRedisTemplate().opsForList();
    }

    /**
     * 获取ValueOperations对象
     * 用于执行与Redis中的值相关的操作
     *
     * @return ValueOperations
     */
    @Override
    public ValueOperations<String, Object> getValueOperations() {
        // 从RedisTemplate中获取操作值的实例
        return getRedisTemplate().opsForValue();
    }

    /**
     * 获取SetOperations对象
     *
     * @return SetOperations
     */
    @Override
    public SetOperations<String, Object> getSetOperations() {
        return getRedisTemplate().opsForSet();
    }

    /**
     * 获取HashOperations对象
     *
     * @return HashOperations
     */
    @Override
    public <T> HashOperations<String, String, T> getHashOperations() {
        return getRedisTemplate().opsForHash();
    }

    /**
     * 获取ZSetOperations对象
     * 用于执行与有序集合相关的操作
     *
     * @return ZSetOperations
     */
    @Override
    public ZSetOperations<String, Object> getZSetOperations() {
        return getRedisTemplate().opsForZSet();
    }

    /**
     * 获取StreamOperations对象
     * 该方法用于获取RedisTemplate中的Stream操作对象，以便进行流数据的处理和操作
     *
     * @return StreamOperations<String, Object, Object> 返回一个StreamOperations对象，用于操作Redis中的流数据
     */
    @Override
    public StreamOperations<String, String, Object> getStreamOperations() {
        return getRedisTemplate().opsForStream();
    }

    /**
     * 获取ClusterOperations对象
     *
     * @return ClusterOperations
     */
    @Override
    public ClusterOperations<String, Object> getClusterOperations() {
        return getRedisTemplate().opsForCluster();
    }

    /**
     * 获取GeoOperations对象
     *
     * @return GeoOperations
     */
    @Override
    public GeoOperations<String, Object> getGeoOperations() {
        return getRedisTemplate().opsForGeo();
    }

    /**
     * 获取HyperLogLogOperations对象
     * 该方法用于从RedisTemplate中获取HyperLogLog操作的实例
     * 主要用于执行HyperLogLog相关的操作，如添加元素、计算近似集合大小等
     *
     * @return HyperLogLogOperations<String, Object> 返回一个HyperLogLog操作的实例
     */
    @Override
    public HyperLogLogOperations<String, Object> getHyperLogLogOperations() {
        return getRedisTemplate().opsForHyperLogLog();
    }

    /**
     * 设置有效时间
     *
     * @param k       储存的数据的键
     * @param subK    储存的数据的子键
     * @param timeout 数据的生命时长 （秒）
     * @return boolean
     */
    @Override
    public boolean expire(String k, String subK, final long timeout) {
        return expire(k, subK, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param k       储存的数据的键
     * @param timeout 数据的生命时长 （秒）
     * @return boolean
     */
    @Override
    public boolean expire(String k, long timeout) {
        // 调用重载方法，设置数据的有效时间为秒级
        return expire(k, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param k       储存的数据的键
     * @param subK    储存的数据的子键
     * @param timeout 数据的生命时长
     * @param unit    时长类型
     * @return boolean
     */
    @Override
    public boolean expire(String k, String subK, final long timeout, final TimeUnit unit) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        // 调用expire方法设置组合键的有效时间
        return expire(key, timeout, unit);
    }

    /**
     * 设置有效时间
     *
     * @param k       储存的数据的键
     * @param timeout 数据的生命时长
     * @param unit    时长类型
     * @return boolean
     */
    @Override
    public boolean expire(String k, long timeout, TimeUnit unit) {
        if (StringUtils.isBlank(k)) {
            //log.error("设置有效时间失败: key为空");
            throw new IllegalStateException("设置有效时间失败: key is null");
        }
        if (unit == null) {
            //log.error("设置有效时间失败: unit不能为空");
            throw new IllegalStateException("设置有效时间失败: unit不能为空");
        }
        try {
            Boolean expire = redisTemplate.expire(k, timeout, unit);
            if (expire == null) {
                log.warn("设置有效时间失败: key[{}] 未找到", k);
                return false;
            }
            return expire;
        } catch (Exception e) {
            log.error("设置有效时间失败: key[{}], error[{}]", k, e.getMessage() + " - " + e.getClass().getName(), e);
            // throw new RuntimeException("设置有效时间失败", e);
        }
        return false;
    }

    /**
     * 设置有效时间
     *
     * @param k             主键，用于标识数据的主键
     * @param subK          子键，用于标识数据的子键
     * @param localDateTime 数据的生命时长 （秒）
     * @return boolean
     */
    @Override
    public boolean expireAt(String k, String subK, LocalDateTime localDateTime) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return expireAt(key, localDateTime);
    }

    @Override
    public boolean expireAt(String k, LocalDate localDate) {
        if (StringUtils.isBlank(k)) {
            //log.error("设置有效时间失败: key为空");
            throw new IllegalStateException("设置有效时间失败: key is null");
        }
        if (localDate == null) {
            //log.error("设置有效时间失败: localDateTime为空");
            throw new IllegalStateException("设置有效时间失败: localDateTime为空");
        }
        try {
            Boolean expire = redisTemplate.expireAt(k, Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()));
            if (expire == null) {
                log.warn("设置有效时间失败: key[{}] 未找到", k);
                return false;
            }
            return expire;
        } catch (Exception e) {
            log.error("设置有效时间失败: key[{}], error[{}]", k, e.getMessage() + " - " + e.getClass().getName(), e);
        }
        return false;
    }

    /**
     * 设置有效时间
     * 将给定键的过期时间设置为日期时间戳。
     *
     * @param k         主键，用于标识数据的主键
     * @param subK      子键，用于标识数据的子键
     * @param localDate 数据的生命时长 （秒）
     * @return boolean
     */
    @Override
    public boolean expireAt(String k, String subK, LocalDate localDate) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return expireAt(key, localDate);
    }

    /**
     * 设置有效时间
     * 将给定键的过期时间设置为日期时间戳。
     *
     * @param k             储存的数据的键
     * @param localDateTime 数据的生命时长 （秒）
     * @return boolean
     */
    @Override
    public boolean expireAt(String k, LocalDateTime localDateTime) {

        if (StringUtils.isBlank(k)) {
            //log.error("设置有效时间失败: key为空");
            throw new IllegalStateException("设置有效时间失败: key is null");
        }
        if (localDateTime == null) {
            //log.error("设置有效时间失败: localDateTime为空");
            throw new IllegalStateException("设置有效时间失败: localDateTime为空");
        }
        try {
            Boolean expire = redisTemplate.expireAt(k, Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()));
            if (expire == null) {
                log.warn("设置有效时间失败: key[{}] 未找到", k);
                return false;
            }
            return expire;
        } catch (Exception e) {
            log.error("设置有效时间失败: key[{}], error[{}]", k, e.getMessage() + " - " + e.getClass().getName(), e);
        }
        return false;
    }

    /**
     * 获取剩余时间
     *
     * @param key 储存的数据的键
     * @return 剩余时间
     */
    @Override
    public long getExpire(String key) {
        if (StringUtils.isBlank(key)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        try {
            Long expire = redisTemplate.getExpire(key);
            if (expire == null) {
                log.warn("获取剩余时间失败: key[{}] 未找到", key);
                return 0;
            }
            return expire;
        } catch (Exception e) {
            log.error("获取剩余时间失败: key[{}], error[{}]", key, e.getMessage() + " - " + e.getClass().getName(), e);
        }
        return 0;
    }

    /**
     * 获取剩余时间
     *
     * @param k    主键，用于标识数据的主键
     * @param subK 子键，用于标识数据的子键
     * @return boolean
     */
    @Override
    public long getExpire(String k, String subK) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return getExpire(key);
    }

    /**
     * 获取剩余时间
     *
     * @param key      储存的数据的键
     * @param timeUnit 时长类型
     * @return 剩余时间
     */
    @Override
    public long getExpire(String key, TimeUnit timeUnit) {
        if (StringUtils.isBlank(key)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        try {
            Long expire = redisTemplate.getExpire(key, timeUnit);
            if (expire == null) {
                log.warn("获取剩余时间失败: key[{}] 未找到", key);
                return 0;
            }
            return expire;
        } catch (Exception e) {
            log.error("获取剩余时间失败: key[{}], error[{}]", key, e.getMessage() + " - " + e.getClass().getName(), e);
        }
        return 0;
    }

    /**
     * 获取剩余时间
     *
     * @param k        主键，用于标识数据的主键
     * @param subK     子键，用于标识数据的子键
     * @param timeUnit 时长类型
     * @return boolean
     */
    @Override
    public long getExpire(String k, String subK, TimeUnit timeUnit) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return getExpire(key, timeUnit);
    }

    /**
     * 判断key是否存在
     * <p>
     * 该方法用于检查给定的键和子键组合是否存在于数据结构中
     * 它通过将键和子键连接起来形成一个复合键，然后检查这个复合键是否存在
     *
     * @param k    主键，用于标识数据的主要类别或范围
     * @param subK 子键，用于在主键下进一步细化标识数据
     * @return boolean 返回一个布尔值，表示复合键是否存在于数据结构中
     */
    public boolean containsKey(String k, String subK) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        // 调用基类或数据结构的containsKey方法，检查复合键是否存在
        return containsKey(key);
    }

    /**
     * 判断key是否存在
     *
     * @param k 储存的数据的键
     * @return boolean
     */
    @Override
    public boolean containsKey(String k) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        try {
            Boolean exists = getRedisTemplate().hasKey(k);
            if (exists == null) {
                return false;
            }
            return exists;
        } catch (Exception e) {
            log.error("判断缓存存在失败: key[{}], error[{}]", k, e.getMessage(), e);
            //throw new RuntimeException("判断缓存存在失败", e);
        }
        return false;
    }

    /**
     * 移除某个k
     * 该方法用于检查给定的键和子键组合是否存在于数据结构中
     * 它通过将键和子键连接起来形成一个复合键，然后检查这个复合键是否存在
     *
     * @param k    主键，用于标识数据的主键
     * @param subK 子键，用于标识数据的子键
     * @return 是否移除成功
     */
    public boolean delKey(String k, String subK) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return delKey(key);
    }

    /**
     * 移除一批
     *
     * @param kSet 储存的数据的键
     * @return 是否移除成功
     */
    @Override
    public long delKey(Set<String> kSet) {
        if (CollectionUtils.isEmpty(kSet)) {
            throw new IllegalStateException("移除一批: kSet为空");
        }
        try {
            Long count = getRedisTemplate().delete(kSet);
            if (count == null) {
                log.warn("移除一批失败: kSet[{}] 未找到", kSet);
                return 0;
            }
            return count;
        } catch (Exception e) {
            log.error("移除一批[" + kSet + ", error[" + e.getMessage() + "]");
            //throw new RuntimeException("移除一批", e);
        }
        return 0;
    }

    /**
     * 移除一批
     *
     * @param k       主键，用于标识数据的主键
     * @param subKSet 子键，用于标识数据的子键
     * @return 是否移除成功
     */
    @Override
    public long delKey(String k, Set<String> subKSet) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        if (CollectionUtils.isEmpty(subKSet)) {
            throw new IllegalStateException("移除一批: kSet为空");
        }
        Set<String> kSet = subKSet.stream().map(subK -> Utils.getKey(k, subK)).collect(Collectors.toSet());
        return delKey(kSet);
    }

    /**
     * 移除某个KEY
     *
     * @param k 储存的数据的键
     * @return boolean
     */
    @Override
    public boolean delKey(String k) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        try {
            Boolean deleted = getRedisTemplate().delete(k);
            if (deleted == null) {
                log.warn("移除某个KEY失败: key[{}] 未找到", k);
                return false;
            }
            return deleted;
        } catch (Exception e) {
            log.error("移除某个KEY[" + k + ", error[" + e.getMessage() + "]");
            //throw new RuntimeException("移除某个KEY", e);
        }
        return false;
    }

    public <T> T get(String k, String subK) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return get(key);
    }

    /**
     * 获取单个k缓存
     *
     * @param k 储存的数据的键
     * @return 获取缓存的value
     */
    @Override
    public <T> T get(String k) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        try {
            Object value = getValueOperations().get(k);
            if (null == value) {
                return null;
            }
            @SuppressWarnings("unchecked")
            T result = (T) value;
            return result;
        } catch (Throwable t) {
            log.error("获取缓存失败key[" + k + ", error[" + t + "]");
        }
        return null;
    }

    /**
     * 缓存set操作
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @return set状态
     */
    public <T> boolean set(String k, String subK, final T v) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return set(key, v);
    }

    /**
     * 缓存set操作
     *
     * @param key 储存的数据的键
     * @param v   储存的数据的值
     * @return boolean
     */
    @Override
    public <T> boolean set(String key, T v) {
        if (StringUtils.isBlank(key)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        try {
            getValueOperations().set(key, v);
            return true;
        } catch (Throwable t) {
            log.error("缓存[" + key + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    /**
     * 缓存set操作
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @return set状态
     */
    public <T> boolean set(String k, String subK, final T v, long timeout) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return set(key, v, timeout);
    }

    /**
     * 缓存set操作
     *
     * @param key     储存的数据的键
     * @param v       储存的数据的值
     * @param seconds 数据的生命时长 （秒）
     * @return set状态
     */
    @Override
    public <T> boolean set(String key, T v, long seconds) {
        return set(key, v, seconds, TimeUnit.SECONDS);
    }

    /**
     * 缓存set操作
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @return set状态
     */
    public <T> boolean set(String k, String subK, final T v, long timeout, TimeUnit unit) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return set(key, v, timeout, unit);
    }

    /**
     * 缓存set操作
     *
     * @param key     储存的数据的键
     * @param v       储存的数据的值
     * @param timeout 数据的生命时长
     * @param unit    时长类型
     * @return set状态
     */
    @Override
    public <T> boolean set(String key, T v, long timeout, TimeUnit unit) {
        if (StringUtils.isBlank(key)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        try {
            getValueOperations().set(key, v, timeout, unit);
            return true;
        } catch (Throwable t) {
            log.error("缓存[" + key + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    /**
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @return <T>boolean
     */
    public <T> boolean setIfAbsent(String k, String subK, final T v) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return setIfAbsent(key, v);
    }

    /**
     * 如果键不存在则新增,存在则不改变已经有的值
     *
     * @param key 储存的数据的键
     * @param v   储存的数据的值
     * @return <T>boolean
     */
    @Override
    public <T> boolean setIfAbsent(String key, T v) {
        if (StringUtils.isBlank(key)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        try {
            Boolean bool = getValueOperations().setIfAbsent(key, v);
            if (null == bool) {
                return false;
            }
            return bool;
        } catch (Throwable t) {
            log.error("缓存[" + key + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    /**
     * 如果键不存在则新增,存在则不改变已经有的值
     * <p>
     * 该方法用于检查给定的键和子键组合是否存在于数据结构中
     * 它通过将键和子键连接起来形成一个复合键，然后检查这个复合键是否存在
     *
     * @param k       主键，用于标识数据的主键
     * @param subK    子键，用于标识数据的子键
     * @param v       储存的数据的值
     * @param seconds 数据的生命时长 （秒）
     * @return <T>boolean
     */
    @Override
    public <T> boolean setIfAbsent(String k, String subK, T v, long seconds) {
        return setIfAbsent(k, subK, v, seconds, TimeUnit.SECONDS);
    }

    /**
     * 如果键不存在则新增,存在则不改变已经有的值
     *
     * @param k       储存的数据的键
     * @param v       储存的数据的值
     * @param seconds 数据的生命时长 （秒）
     * @return <T>boolean
     */
    @Override
    public <T> boolean setIfAbsent(String k, T v, long seconds) {
        return setIfAbsent(k, v, seconds, TimeUnit.SECONDS);
    }

    /**
     * 如果键不存在则新增,存在则不改变已经有的值
     * <p>
     * 该方法用于检查给定的键和子键组合是否存在于数据结构中
     * 它通过将键和子键连接起来形成一个复合键，然后检查这个复合键是否存在
     *
     * @param k        主键，用于标识数据的主键
     * @param subK     子键，用于标识数据的子键
     * @param v        储存的数据的值
     * @param time     存活时间
     * @param timeUnit 时间单位
     * @return <T>boolean
     */
    @Override
    public <T> boolean setIfAbsent(String k, String subK, T v, long time, TimeUnit timeUnit) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return setIfAbsent(key, v, time, timeUnit);
    }

    /**
     * 如果键不存在则新增,存在则不改变已经有的值
     *
     * @param k        储存的数据的键
     * @param v        储存的数据的值
     * @param time     存活时间
     * @param timeUnit 时间单位
     * @return <T>boolean
     */
    @Override
    public <T> boolean setIfAbsent(String k, T v, long time, TimeUnit timeUnit) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        try {
            Boolean bool = getValueOperations().setIfAbsent(k, v, time, timeUnit);
            if (null == bool) {
                return false;
            }
            return bool;
        } catch (Throwable t) {
            log.error("缓存[" + k + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    /**
     * 计数器
     * <p>
     * 该方法用于检查给定的键和子键组合是否存在于数据结构中
     * 它通过将键和子键连接起来形成一个复合键，然后检查这个复合键是否存在
     *
     * @param k     主键，用于标识数据的主键
     * @param subK  子键，用于标识数据的子键
     * @param delta 累计数
     * @return 结果
     */
    public Long increment(String k, String subK, Long delta) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return increment(key, delta);
    }

    /**
     * 计数器
     *
     * @param k     key
     * @param delta 累计数
     * @return 结果
     */
    @Override
    public Long increment(String k, Long delta) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        if (null == delta) {
            throw new IllegalStateException("计数器: 累计数为空");
        }
        try {
            return getValueOperations().increment(k, delta);
        } catch (Throwable t) {
            log.error("计数器出错key[" + k + ", error[" + t + "]");
        }
        return null;
    }

    /**
     * 计数器
     *
     * @param k       储存的数据的键
     * @param subK    储存的数据的键
     * @param delta   累计数
     * @param seconds 存活时间（秒）
     * @return 结果
     */
    @Override
    public Long increment(String k, String subK, Long delta, long seconds) {
        return increment(k, subK, delta, seconds, TimeUnit.SECONDS);
    }

    /**
     * 计数器
     *
     * @param k       k
     * @param delta   累加次数
     * @param seconds 存活时间（秒）
     * @return 结果
     */
    @Override
    public Long increment(String k, Long delta, long seconds) {
        return increment(k, delta, seconds, TimeUnit.SECONDS);
    }

    /**
     * 计数器
     *
     * @param k        储存的数据的键
     * @param subK     储存的数据的键
     * @param delta    累计数
     * @param time     存活时间
     * @param timeUnit 时间单位
     * @return 结果
     */
    @Override
    public Long increment(String k, String subK, Long delta, long time, TimeUnit timeUnit) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return increment(key, delta, time, timeUnit);
    }

    /**
     * 计数器
     *
     * @param k        储存的数据的键
     * @param delta    累计数
     * @param time     存活时间
     * @param timeUnit 时间单位
     * @return 结果
     */
    @Override
    public Long increment(String k, Long delta, long time, TimeUnit timeUnit) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        if (delta == null) {
            throw new IllegalArgumentException("累计数不能为null");
        }
        if (timeUnit == null) {
            throw new IllegalArgumentException("时间单位不能为null");
        }
        Long increment = increment(k, delta);
        if (null == increment) {
            return null;
        }
        // 设置过期时间
        expire(k, time, timeUnit);
        return increment;
    }

    /**
     * 计数器
     * <p>
     * 该方法用于检查给定的键和子键组合是否存在于数据结构中
     * 它通过将键和子键连接起来形成一个复合键，然后检查这个复合键是否存在
     *
     * @param k     主键，用于标识数据的主键
     * @param subK  子键，用于标识数据的子键
     * @param delta 累计数
     * @return 结果
     */
    @Override
    public Double increment(String k, String subK, double delta) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return increment(key, delta);
    }

    /**
     * 计数器
     *
     * @param k     主键，用于标识数据的主键
     * @param delta 累计数
     * @return 结果
     */
    @Override
    public Double increment(String k, double delta) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        try {
            return getValueOperations().increment(k, delta);
        } catch (Throwable t) {
            log.error("计数器出错key[" + k + ", error[" + t + "]");
        }
        return null;
    }

    /**
     * 计数器
     *
     * @param k       主键，用于标识数据的主键
     * @param subK    子键，用于标识数据的子键
     * @param delta   累计数
     * @param seconds 存活时间（秒）
     * @return 结果
     */
    @Override
    public Double increment(String k, String subK, double delta, long seconds) {
        return increment(k, subK, delta, seconds, TimeUnit.SECONDS);
    }

    /**
     * 计数器
     *
     * @param k       k
     * @param delta   累计数
     * @param seconds 存活时间（秒）
     * @return 结果
     */
    @Override
    public Double increment(String k, double delta, long seconds) {
        return increment(k, delta, seconds, TimeUnit.SECONDS);
    }

    /**
     * 计数器
     *
     * @param k        储存的数据的键
     * @param subK     储存的数据的键
     * @param delta    累计数
     * @param time     存活时间
     * @param timeUnit 时间单位
     * @return 结果
     */
    @Override
    public Double increment(String k, String subK, double delta, long time, TimeUnit timeUnit) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return increment(key, delta, time, timeUnit);
    }

    /**
     * 计数器
     *
     * @param k        储存的数据的键
     * @param delta    累计数
     * @param time     存活时间
     * @param timeUnit 时间单位
     * @return 结果
     */
    @Override
    public Double increment(String k, double delta, long time, TimeUnit timeUnit) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        if (timeUnit == null) {
            throw new IllegalArgumentException("时间单位不能为null");
        }
        Double increment = increment(k, delta);
        if (null == increment) {
            return null;
        }
        expire(k, time, timeUnit);
        return increment;
    }

    /**
     * 计数器
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @return 结果
     */
    public Long increment(String k, String subK) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return increment(key);
    }

    /**
     * 计数器
     *
     * @param k 储存的数据的键
     * @return 结果
     */
    @Override
    public Long increment(String k) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        try {
            return getValueOperations().increment(k);
        } catch (Throwable t) {
            log.error("计数器出错key[" + k + ", error[" + t + "]");
        }
        return null;
    }

    /**
     * 计数器
     *
     * @param k       储存的数据的键
     * @param subK    储存的数据的键
     * @param seconds 存活时间（秒）
     * @return 结果
     */
    @Override
    public Long increment(String k, String subK, long seconds) {
        return increment(k, subK, seconds, TimeUnit.SECONDS);
    }

    /**
     * 计数器
     *
     * @param k       储存的数据的键
     * @param seconds 存活时间（秒）
     * @return 结果
     */
    @Override
    public Long increment(String k, long seconds) {
        return increment(k, seconds, TimeUnit.SECONDS);
    }

    /**
     * 计数器
     *
     * @param k        储存的数据的键
     * @param subK     储存的数据的键
     * @param time     存活时间
     * @param timeUnit 时间单位
     * @return 结果
     */
    public Long increment(String k, String subK, long time, TimeUnit timeUnit) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return increment(key, time, timeUnit);
    }

    /**
     * 计数器
     *
     * @param k        储存的数据的键
     * @param time     存活时间
     * @param timeUnit 时间单位
     * @return 结果
     */
    @Override
    public Long increment(String k, long time, TimeUnit timeUnit) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        if (null == timeUnit) {
            throw new IllegalArgumentException("时间单位不能为null");
        }
        Long increment = increment(k);
        if (null == increment) {
            return null;
        }
        this.expire(k, time, timeUnit);
        return increment;
    }

    /**
     * 缓存Hash
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @return 缓存的结果
     */
    @Override
    public <T> boolean putAll(String k, String subK, T v) {
        String key = Utils.getKey(k, subK);
        return putAll(key, v);
    }

    /**
     * 缓存Hash
     *
     * @param k 储存的数据的键
     * @param v 储存的数据的值
     * @return 缓存的结果
     */
    @Override
    public <T> boolean putAll(String k, T v) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        try {
            Map<String, Object> map = Utils.beanToMap(v);
            putAll(k, map);
            return true;
        } catch (Exception e) {
            log.error("缓存[" + k + "]失败, value[" + v + "]", e);
        }
        return false;
    }

    /**
     * 缓存hash
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @return 缓存的结果
     */
    @Override
    public <T> boolean putAll(String k, String subK, Map<String, T> v) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return putAll(key, v);
    }

    /**
     * 缓存hash
     *
     * @param k 储存的数据的键
     * @param v 储存的数据的值
     * @return 缓存的结果
     */
    @Override
    public <T> boolean putAll(String k, Map<String, T> v) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        try {
            getHashOperations().putAll(k, v);
            return true;
        } catch (Exception e) {
            log.error("缓存[" + k + "]失败, value[" + v + "]", e);
        }
        return false;
    }

    /**
     * 缓存hash
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @return 缓存的结果
     */
    @Override
    public <T> boolean putAll(String k, String subK, Map<String, T> v, long time) {
        return putAll(k, subK, v, time, TimeUnit.SECONDS);
    }

    /**
     * 缓存hash
     *
     * @param k    储存的数据的键
     * @param v    储存的数据的值
     * @param time 数据的生命时长
     * @return 缓存的结果
     */
    @Override
    public <T> boolean putAll(String k, Map<String, T> v, long time) {
        return putAll(k, v, time, TimeUnit.SECONDS);
    }

    /**
     * 缓存Hash操作
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @param time 数据的生命时长
     * @return set状态
     */
    @Override
    public <T> boolean putAll(String k, String subK, T v, long time) {
        return putAll(k, subK, v, time, TimeUnit.SECONDS);
    }

    /**
     * 缓存Hash操作
     *
     * @param k    储存的数据的键
     * @param v    储存的数据的值
     * @param time 数据的生命时长
     * @return set状态
     */
    @Override
    public <T> boolean putAll(String k, T v, long time) {
        return putAll(k, v, time, TimeUnit.SECONDS);
    }

    /**
     * 缓存Hash操作
     *
     * @param k       储存的数据的键
     * @param subK    储存的数据的键
     * @param v       储存的数据的值
     * @param timeout 数据的生命时长
     * @param unit    时长类型
     * @return boolean
     */
    @Override
    public <T> boolean putAll(String k, String subK, T v, long timeout, TimeUnit unit) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return putAll(key, v, timeout, unit);
    }

    /**
     * 缓存Hash操作
     *
     * @param k       储存的数据的键
     * @param v       储存的数据的值
     * @param timeout 数据的生命时长
     * @param unit    时长类型
     * @return boolean
     */
    @Override
    public <T> boolean putAll(String k, T v, long timeout, TimeUnit unit) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        boolean b = putAll(k, v);
        if (!b) {
            return false;
        }
        expire(k, timeout, unit);
        return true;
    }

    /**
     * 缓存Hash操作
     *
     * @param k       储存的数据的键
     * @param subK    储存的数据的键
     * @param v       储存的数据的值
     * @param timeout 数据的生命时长
     * @param unit    时长类型
     * @return boolean
     */
    @Override
    public <T> boolean putAll(String k, String subK, Map<String, T> v, long timeout, TimeUnit unit) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return putAll(key, v, timeout, unit);
    }

    /**
     * 缓存Hash操作
     *
     * @param k       储存的数据的键
     * @param v       储存的数据的值
     * @param timeout 数据的生命时长
     * @param unit    时长类型
     * @return boolean
     */
    @Override
    public <T> boolean putAll(String k, Map<String, T> v, long timeout, TimeUnit unit) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        if (null == unit) {
            throw new IllegalArgumentException("时间单位不能为null");
        }
        try {
            boolean ok = putAll(k, v);
            if (!ok) {
                return false;
            }
            expire(k, timeout, unit);
            return true;
        } catch (Exception e) {
            log.error("缓存[" + k + "]失败, value[" + v + "]", e);
        }
        return false;
    }

    /**
     * 获取单个k缓存
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @return 获取缓存的value
     */
    @Override
    public <T> T getHash(String k, String subK, Class<T> clazz) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return getHash(key, clazz);
    }

    /**
     * 获取单个k缓存
     *
     * @param k 储存的数据的键
     * @return 获取缓存的value
     */
    @Override
    public <T> T getHash(String k, Class<T> clazz) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        try {
            Map<String,Object> entries = getHashOperations().entries(k);
            return Utils.mapToBean(entries, clazz);
        } catch (Exception e) {
            log.error("获取[" + k + "]失败", e);
        }
        return null;
    }

    /**
     * 获取单个k缓存
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @return 获取缓存的value
     */
    @Override
    public <T> Map<String, T> getHash(String k, String subK) {
        // 组合键和子键以形成完整的键
        String key = Utils.getKey(k, subK);
        return getHash(key);
    }

    /**
     * 获取单个k缓存
     *
     * @param k 储存的数据的键
     * @return 获取缓存的value
     */
    @Override
    public <T> Map<String, T> getHash(String k) {
        if (StringUtils.isBlank(k)) {
            throw new IllegalStateException("判断key是否存在: key为空");
        }
        try {
            Map<String, T> entries = (Map<String, T>) getHashOperations().entries(k);
            return entries;
        } catch (Exception e) {
            log.error("获取[" + k + "]失败", e);
        }
        return null;
    }

    /**
     * 获取单个k缓存
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @return 获取缓存的value
     */
//    @Override
//    public <T> T getHash(String k, String subK, SFunction<T, ?>... fn) {
//        return null;
//    }

    /**
     * 获取单个k缓存
     *
     * @param k 储存的数据的键
     * @return 获取缓存的value
     */
//    @Override
//    public <T> T getHash(String k, SFunction<T, ?>... fn) {
//        return null;
//    }

    /**
     * 缓存set
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @return 缓存的结果
     */
    public <T> boolean cacheSet(String k, String subK, final T v) {
        String key = k + ":" + subK;
        return cacheSet(key, subK, v, -1);
    }

    /**
     * 缓存set操作
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @param time 数据的生命时长
     * @return set状态
     */
    public <T> boolean cacheSet(String k, String subK, final T v, long time) {
        String key = k + ":" + subK;
        try {
            getRedisTemplate().opsForSet().add(key, v);
            if (time > 0) {
                expire(key, subK, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Throwable t) {
            log.error("缓存[" + key + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    /**
     * 缓存set
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @return 存入的状态
     */
    public <T> boolean cacheSet(String k, String subK, Set<T> v) {
        return cacheSet(k, subK, v, -1);
    }

    /**
     * 缓存set
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @param time 存入的时间
     * @return 存入的转台
     */
    public <T> boolean cacheSet(String k, String subK, Set<T> v, long time) {
        String key = k + ":" + subK;
        try {
            getRedisTemplate().opsForSet().add(key, v);
            if (time > 0) {
                expire(key, subK, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Throwable t) {
            log.error("缓存[" + key + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    /**
     * 获取缓存set数据
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @return 缓存的值
     */
    public <T> Set<T> getSet(String k, String subK) {
        String key = k + ":" + subK;
        try {
            return (Set<T>) getRedisTemplate().opsForSet().members(key);
        } catch (Throwable t) {
            log.error("获取set缓存失败key[" + key + ", error[" + t + "]");
        }
        return null;
    }

    /**
     * 缓存list
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @return 缓存一个集合
     */
    public <T> boolean cacheList(String k, String subK, final T v) {
        return cacheList(k, subK, v, -1);
    }

    /**
     * 向一个list集合中添加新项
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @param time 缓存的时间
     * @return 缓存的状态
     */
    public <T> boolean cacheList(String k, String subK, final T v, long time) {
        String key = k + ":" + subK;
        try {
            getRedisTemplate().opsForList().rightPush(key, v);
            if (time > 0) {
                getRedisTemplate().expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Throwable t) {
            log.error("缓存[" + key + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    /**
     * 缓存list
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @return 缓存状态
     */
    public <T> boolean cacheList(String k, String subK, List<T> v) {
        return cacheList(k, subK, v, -1);
    }

    /**
     * 缓存一个list
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @param time 缓存的时间
     * @return 缓存的状态
     */
    public <T> boolean cacheList(String k, String subK, List<T> v, long time) {
        String key = k + ":" + subK;
        try {
            getRedisTemplate().opsForList().rightPushAll(k, v);
            if (time > 0) {
                getRedisTemplate().expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Throwable t) {
            log.error("缓存[" + key + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    /**
     * 向左缓存一个list
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @return 缓存的状态
     */
    public <T> boolean cacheLeftList(String k, String subK, List<T> v) {
        return cacheLeftList(k, subK, v, -1);
    }

    /**
     * 获取总条数, 可用于分页
     *
     * @param listOps 获取集合的size
     * @param k       储存的数据的键
     * @return 集合的长度
     */
/*    public long getListSize(ListOperations<String, String> listOps, String k) {
        try {
            return listOps.size(k);
        } catch (Throwable t) {
            log.error("获取list长度失败key[" + k + "], error[" + t + "]");
        }
        return 0;
    }*/

    /**
     * 向左缓存一个list
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @param time 缓存的时间
     * @return 缓存的状态
     */
    public <T> boolean cacheLeftList(String k, String subK, List<T> v, long time) {
        String key = k + ":" + subK;
        try {
            getRedisTemplate().opsForList().leftPushAll(key, v);
            if (time > 0) {
                getRedisTemplate().expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Throwable t) {
            log.error("缓存[" + key + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    public <T> List<T> getList(String k, String subK) {
        return getList(k, subK, 0, -1);
    }

    /**
     * 获取list缓存
     *
     * @param k     储存的数据的键
     * @param subK  储存的数据的键
     * @param start 开始获取的位置
     * @param end   结束获取的位置
     * @return 集合
     */
    public <T> List<T> getList(String k, String subK, long start, long end) {
        String key = k + ":" + subK;
        try {
            return (List<T>) getRedisTemplate().opsForList().range(key, start, end);
        } catch (Throwable t) {
            log.error("获取list缓存失败key[" + k + ", error[" + t + "]");
        }
        return null;
    }

    /**
     * 获取总条数, 可用于分页
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @return 集合的长度
     */
    public long getListSize(String k, String subK) {
        String key = k + ":" + subK;
        try {
            return getRedisTemplate().opsForList().size(key);
        } catch (Throwable t) {
            log.error("获取list长度失败key[" + key + "], error[" + t + "]");
        }
        return 0;
    }

    /**
     * 移除并list中的首项
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @return 移除结果
     */
    public <T> boolean removeOneOfList(String k, String subK) {
        String key = k + ":" + subK;
        try {
            getRedisTemplate().opsForList().rightPop(key);
            return true;
        } catch (Throwable t) {
            log.error("移除list缓存失败key[" + key + ", error[" + t + "]");
        }
        return false;
    }

    /**
     * 移除对应的 value
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    值
     * @return
     */
    public <T> boolean removeListValue(String k, String subK, T v) {
        String key = k + ":" + subK;
        try {
            getRedisTemplate().opsForList().remove(key, 0, v);
            return true;
        } catch (Throwable t) {
            log.error("移除list缓存失败key[" + key + ", error[" + t + "]");
        }
        return false;
    }

    /**
     * 移除对应的 value
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param list list
     * @return
     */
    public <T> boolean removeList(String k, String subK, List<String> list) {
        String key = k + ":" + subK;
        try {
            ListOperations<String, Object> listOps = getRedisTemplate().opsForList();
            for (int i = 0; i < list.size(); i++) {
                final T value = (T) list.get(i);
                listOps.remove(key, 0, value);
            }
            return true;
        } catch (Throwable t) {
            log.error("移除list缓存失败key[" + key + ", error[" + t + "]");
        }
        return false;
    }

    /**
     * 获取byte数据
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @return byte[]
     */
    public byte[] getByte(String k, String subK) {
        String key = k + subK;
        try {
            // 使用了lambda表达式
            return getRedisTemplate().execute((RedisConnection redisConnection) -> redisConnection.get(key.getBytes()));
        } catch (Exception e) {
            log.error("获取缓存失败key[" + key + ", error[" + e.getMessage() + "]");
        }
        return null;
    }

    /**
     * 缓存byte数据
     *
     * @param k      储存的数据的键
     * @param subK   储存的数据的键
     * @param tClass 转换的类型
     * @return <T>boolean
     */
    public <T> T getByte(String k, String subK, Class<T> tClass) {
        byte[] aByte = getByte(k, subK);
        if (null == aByte) {
            return null;
        }
        String str = JSON.toJSONString(toObject(aByte));
        return JSON.parseObject(str, tClass);
    }

    /**
     * 缓存byte数据
     *
     * @param k       储存的数据的键
     * @param subK    储存的数据的键
     * @param v       储存的数据的值
     * @param timeout 数据的生命时长
     * @param unit    时长类型
     * @return boolean
     */
    public <T> boolean setByte(String k, String subK, final T v, long timeout, TimeUnit unit) {
        String key = k + subK;
        try {
            Expiration expiration = Expiration.from(timeout, unit);
            // 使用了lambda表达式
            getRedisTemplate().execute((RedisConnection redisConnection) -> redisConnection.set(key.getBytes(), serialize(v), expiration, UPSERT));
            return true;
        } catch (Throwable t) {
            log.error("缓存[" + key + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    /**
     * 缓存byte数据
     *
     * @param k    储存的数据的键
     * @param subK 储存的数据的键
     * @param v    储存的数据的值
     * @return <T>boolean
     */
    public <T> boolean setByte(String k, String subK, final T v) {
        String key = k + subK;
        try {

            // 使用了lambda表达式
            getRedisTemplate().execute((RedisConnection redisConnection) -> redisConnection.set(key.getBytes(), serialize(v)));
            return true;
        } catch (Throwable t) {
            log.error("缓存[" + key + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    private static Object toObject(byte[] bytes) {
        Object obj = null;
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bis);
            obj = ois.readObject();
            ois.close();
            bis.close();
        } catch (IOException | ClassNotFoundException ex) {
            //ex.printStackTrace();
            log.error("toObject error", ex);
        }
        return obj;
    }

    private static byte[] serialize(Object value) {
        if (value == null) {
            throw new NullPointerException("Can't serialize null");
        } else {
            byte[] rv = null;
            ByteArrayOutputStream bos = null;
            ObjectOutputStream os = null;

            try {
                bos = new ByteArrayOutputStream();
                os = new ObjectOutputStream(bos);
                os.writeObject(value);
                os.close();
                bos.close();
                rv = bos.toByteArray();
            } catch (IOException var8) {
                throw new IllegalArgumentException("Non-serializable object", var8);
            } finally {
                try {
                    if (bos != null) {
                        bos.close();
                    }
                    if (os != null) {
                        os.close();
                    }
                } catch (IOException e) {
                    log.error("close stream error", e);
                    //e.printStackTrace();
                }
            }

            return rv;
        }
    }
}
